#pragma once

#include <iostream>

#include <unordered_map>

#include <pthread.h> // 需要用读写锁

#include "util.hpp"

#include "config.hpp"

namespace Cloud{
typedef struct BackupInfo_t{ // 文件信息
    bool pack_flag; // 用于判断文件是否被压缩
    size_t fsize; // 文件大小
    time_t mtime; // 文件最后修改时间
    time_t atime; // 文件最后访问时间
    std::string real_path; // 文件实际路径(实际存储的路径)
    std::string pack_path; // 文件压缩包存储路径
    std::string url; // 下载访问链接 url
    bool NewBackupInfo(const std::string &realpath);   
}BackupInfo;

bool BackupInfo::NewBackupInfo(const std::string &realpath){
    // 根据传入的 realpath 获取数据 (类似初始化)
    FileUtil fu(realpath);
    Config* config = Config::GetInstance();
    
    if(!fu.Exists()){
        std::cerr<<"NewBackupInfo failed, file no exists!!"<<std::endl;
        return false;
    }

    std::string packdir = config->GetPackDir(); // 压缩包路径
    std::string filename = fu.FileName(); // 文件名
    std::string packsuffix = config->GetPackFileSuffix(); // 压缩后缀
    std::string download_prefix = config->GetDownloadPrefix(); // 下载路径前缀

    this->pack_flag = false;
    this->fsize = fu.FileSize();
    this->mtime = fu.LastMTime();
    this->atime = fu.LastATime();
    this->real_path = realpath;
    
    // pack_bath = 压缩包路径+文件名+后缀
    this->pack_path = packdir + filename + packsuffix; 

    // url = 下载路径前缀 + 文件名 
    this->url = download_prefix + filename ;
    return true;
}



class DataManager{ // 数据管理模块
private:
    std::string _backup_file; // 持久化存储文件信息 - 在配置信息中
    pthread_rwlock_t _rwlock; // 读写锁 - 写互斥
    std::unordered_map<std::string, BackupInfo> _table; // 表 - 用来存储上传文件信息和路径之间的映射关系

public:
    DataManager(); // 构造函数 用来初始化持久化存储文件信息和读写锁
    ~DataManager(); // 析构函数 主要是用来释放读写锁

    bool Insert(const BackupInfo &info); // 插入 BackupInfo
    bool Update(const BackupInfo &info); // 更新 BackupInfo
    bool GetOneByURL(const std::string &url, BackupInfo *info); // 根据 Url 获取对应的 BackupInfo 信息
    bool GetOneByRealPath(const std::string &realpath,  BackupInfo *info); // 从 realpath 中获取对应的 BackupInfo
    bool GetAll(std::vector<BackupInfo> *array); // 获取所有的 BackupInfo
    bool Storage(); // 持久化存储
    bool InitLoad();
                                                       
};

DataManager::DataManager(){
    _backup_file = Config::GetInstance()->GetBackupFile(); // 从配置文件中获取持久化存储的文件
    pthread_rwlock_init(&_rwlock,nullptr); // 初始化读写锁
    InitLoad(); // 初始化数据
}


DataManager::~DataManager(){
    pthread_rwlock_destroy(&_rwlock); // 释放读写锁
}


bool DataManager:: Insert(const BackupInfo &info){
    pthread_rwlock_wrlock(&_rwlock);
    _table[info.url] = info; // 这里是存储下载路径与对应信息的映射关系
                             // 客户端发送一个下载请求 请求通过哈希表找到对应的存储信息
    pthread_rwlock_unlock(&_rwlock);
    this->Storage();
    return true;
}

bool DataManager:: Update(const BackupInfo &info){
    // 目前这里的更新与插入无异 即覆盖原有的存档
    pthread_rwlock_wrlock(&_rwlock);
    _table[info.url] = info;
    pthread_rwlock_unlock(&_rwlock);
    this->Storage();
    return true;
}
    
bool DataManager:: GetOneByURL(const std::string &url, BackupInfo *info){
    pthread_rwlock_wrlock(&_rwlock);
    auto it = _table.find(url);
    // 哈希表中的 key 值为 url ,可以直接 find
    if(it == _table.end()){
        pthread_rwlock_unlock(&_rwlock);
        return false;
    }
    *info = it->second;
    pthread_rwlock_unlock(&_rwlock);
    return true;
}
    
bool DataManager:: GetOneByRealPath(const std::string &realpath,  BackupInfo *info){
    // 需要将 realpath 转成 url
    pthread_rwlock_wrlock(&_rwlock);
    auto it = _table.begin();
    for(; it!=_table.end() ; ++it){
        if(realpath == it->second.real_path){ // 判断所传入的 realpath 是否存在于 _table 中的某个 BackupInfo 中
            *info = it->second;
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
    }
    pthread_rwlock_unlock(&_rwlock);
    return false;
}
    
bool DataManager:: GetAll(std::vector<BackupInfo> *array){
    // 可以使用范围 for  也可以使用迭代器 (本质上就是迭代器)
    pthread_rwlock_wrlock(&_rwlock);
    for(auto &it : _table){
        array->push_back(it.second); // 范围 for 这里遍历出来的是一个 pair 类型
                                     // 不能用指向符 ->
    }
    pthread_rwlock_unlock(&_rwlock);
    return true;
}

bool DataManager::Storage(){
    std::vector<Cloud::BackupInfo> infos; // 定义一个数组 
                                          // 用来接收哈希表内所有数据
    // 获取数据
    this->GetAll(&infos);

    // Json 数组根
    Json::Value root;

    // 循环将遍历的数据加入至 root 
    for(auto &it : infos){
        Json::Value item;
        item["pack_flag"] = it.pack_flag;
        item["fsize"] = (Json::Int64)it.fsize;
        item["mtime"] = (Json::Int64)it.mtime;
        item["atime"] = (Json::Int64)it.atime;
        item["real_path"] = it.real_path;
        item["pack_path"] = it.pack_path;
        item["url"] = it.url;
        root.append(item); // 
    }

    // 进行序列化
    std::string body;
    JsonUtil::Serialize(root,&body); 

    // 写入至文件
    FileUtil fu(_backup_file);
    fu.SetContent(body);
    return true;
}

bool  DataManager::InitLoad(){
    // 读取文件
    FileUtil fu(_backup_file);
    if(!fu.Exists()){
        // 说明当前不存在对应文件
        return true;
    }
    
    std::string body;
    fu.GetContent(&body);

    // 反序列化
    Json::Value root;
    JsonUtil::UnSerialize(body, &root);

    // 将反序列化后的数据进行赋值
    for(unsigned int i = 0;i<root.size();++i){
        BackupInfo item;
        Json::Value tmp = root[i];
        item.pack_flag = tmp["pack_flag"].asBool();
        item.atime = tmp["atime"].asInt64();
        item.mtime= tmp["mtime"].asInt64();
        item.pack_path= tmp["pack_path"].asString();
        item.real_path= tmp["real_path"].asString();
        item.url= tmp["url"].asString();
        item.fsize= tmp["fsize"].asInt64();
        Insert(item);
    }    
    return true;
}


}// namespace Cloud 

